package com.duqc.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/**
 * @ClassName GroupChatServer
 * @Description GroupChatServer
 * @Author duqc
 * @Date 2021/8/24
 **/
public class GroupChatServer {

    private Selector selector;

    private ServerSocketChannel serverSocketChannel;

    public static final int PORT = 6667;

    /**
     * 构造器初始化成员变量
     */
    public GroupChatServer() {
        try {
            //打开一个选择器
            this.selector = Selector.open();
            //打开ServerSocketChannel
            this.serverSocketChannel = ServerSocketChannel.open();
            //绑定地址、端口号
            this.serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", PORT));
            //设置为非阻塞
            serverSocketChannel.configureBlocking(false);
            //注册通道到选择器中
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听并接收客户端消息，转发到其他客户端
     */
    public void listen() {
        try {
            while (true) {
                //获取监听的时间总数
                int count = selector.select(2000);
                if (count > 0) {
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    //获取selectionKey集合
                    Iterator<SelectionKey> iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        //如果是获取连接事件
                        if (key.isAcceptable()) {
                            SocketChannel socketChannel = serverSocketChannel.accept();
                            //设置为非阻塞
                            socketChannel.configureBlocking(false);
                            //注册到选择器中
                            socketChannel.register(selector, SelectionKey.OP_READ);
                            System.out.println(socketChannel.getRemoteAddress() + "上线了");
                        }
                        //如果是读就绪事件
                        if (key.isReadable()) {
                            //读取消息，并转发到其他客户端
                            readData(key);
                        }
                        iterator.remove();
                    }
                } else {
                    System.out.println("等待：");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取客户端发送过来的消息
     *
     * @param key
     */
    private void readData(SelectionKey key) {
        SocketChannel socketChannel = null;
        try {
            //从selectorKey中获取channel
            socketChannel = (SocketChannel) key.channel();
            //创建一个缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            //把通道的数据写入到缓冲区
            int count = socketChannel.read(byteBuffer);
            //判断返回的count是否大于0，大于0表示读取到了数据
            if (count > 0) {
                //把缓冲区的byte[]转成字符串
                String s = new String(byteBuffer.array());
                System.out.println("from 客户端：" + s);
                //转发到其他客户端
                notifyAllClient(s, socketChannel);
            }
        } catch (Exception e) {
            try {
                System.out.println(socketChannel.getRemoteAddress() + "离线了...");
                key.cancel();
                socketChannel.close();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }

        }
    }

    /**
     * 转发消息到其他客户端
     *
     * @param s             消息
     * @param socketChannel 不需要通知的Channel
     */
    private void notifyAllClient(String s, SocketChannel socketChannel) throws Exception {
        System.out.println("客户端转发消息");
        selector.keys().forEach(selectionKey -> {
            Channel channel = selectionKey.channel();
            if (channel instanceof SocketChannel && channel != socketChannel) {
                SocketChannel socketChannel1 = (SocketChannel) channel;
                //通过消息，包裹获取一个缓冲区
                ByteBuffer wrap = ByteBuffer.wrap(s.getBytes());
                try {
                    socketChannel1.write(wrap);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public static void main(String[] args) {
        GroupChatServer groupChatServer = new GroupChatServer();
        groupChatServer.listen();
    }

}
